package server.connector.http;


import server.connector.HttpResponseFacade;
import server.connector.ResponseStream;
import server.util.CookieUtil;
import server.util.ResponseUtil;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
//sendLineAndHeaders方法后续要改动
//isEncodeable方法后续要改动
public abstract class HttpResponseBase {
    //不懂得
    //Are we currently processing inside a RequestDispatcher.include()?
    protected boolean included = false;
    protected boolean suspended = false;
    //------------------------------------------------------------------
    //没写完整的
    //发送响应行和响应头 发送后committed变为true
    protected void sendLineAndHeaders() {
        HttpServletResponse response = (HttpServletResponse)this;
        //已经发送,直接返回
        if (response.isCommitted())
            return;
        //HTTP/0.9协议没用请求头
        if ("HTTP/0.9".equals(request.getProtocol())) {
            committed = true;
            return;
        }
        //使用指定的编码发送
        OutputStreamWriter osr = null;
        try {
            osr = new OutputStreamWriter(getStream(), response.getCharacterEncoding());
        } catch (UnsupportedEncodingException e) {
            osr = new OutputStreamWriter(getStream());
        }
        PrintWriter writer = new PrintWriter(osr);
        //发送响应行
        writer.print(getProtocol() + " " + status);
        if (message != null) {
            writer.print(" " + message);
        }
        writer.print("\r\n");
        //发送响应头
        if (response.getContentType() != null)
            writer.print("Content-Type: " + response.getContentType() + "\r\n");
        if (getContentLength() >= 0) {
            writer.print("Content-Length: " + getContentLength() + "\r\n");
        }
        synchronized (headers) {
            for (Map.Entry<String, ArrayList<String>> entry : headers.entrySet()) {
                String name = entry.getKey();
                if ("Content-Type".equalsIgnoreCase(name) || "Content-Length".equalsIgnoreCase(name))
                    continue;
                ArrayList<String> values = entry.getValue();
                for (String value : values) {
                    writer.print(name + ": " + value + "\r\n");
                }
            }
        }
        //添加sessionID cookie
        HttpServletRequest hsq = ((HttpServletRequest) request);
        HttpSession session = hsq.getSession(false);
        if (session != null && session.isNew()
//后续改                && (getContext() != null)
//后续改                && getContext().getCookies()
        ) {
            Cookie cookie = new Cookie("JSESSIONID", session.getId());
            //临时性cookie,浏览器关闭即销毁,不会持久化保存
            cookie.setMaxAge(-1);

            String contextPath = null;
//后续改           if (context != null)
//后续改                contextPath = context.getPath();
            if ((contextPath != null) && (contextPath.length() > 0))
                cookie.setPath(contextPath);
            else
                cookie.setPath("/");
            if (hsq.isSecure())
                cookie.setSecure(true);
            response.addCookie(cookie);
        }
        //发送cookie
        synchronized (cookies) {
            for (Cookie cookie : cookies) {
                writer.print(CookieUtil.getCookieHeaderName(cookie) +
                        ": " +
                        CookieUtil.getCookieHeaderValue(cookie) + "\r\n");
            }
        }
        //发送空行
        writer.print("\r\n");
        writer.flush();
        //设置已提交
        committed = true;
    }
    //是否可以重写URL
    //不完整,后续改
    protected boolean isEncodeable(String location) {
        if (location == null)
            return false;
        if (location.startsWith("#"))
            return false;
        HttpSession session = request.getSession(false);
        //没有session,无法发送sessionID
        if (session == null)
            return false;
        //sessionID来自Cookie,说明客户端不禁用Cookie,不需要重写URL
        if (request.isRequestedSessionIdFromCookie())
            return false;

        //如果location格式不合法,无法重写
        URL url = null;
        try {
            url = new URL(location);
        } catch (MalformedURLException e) {
            return false;
        }
        //协议不一样,无法重写
        if (!request.getScheme().equalsIgnoreCase(url.getProtocol()))
            return false;
        //主机名不一样,无法重写
        if (request.getServerName().equalsIgnoreCase(url.getHost()))
            return false;
        int serverPort = request.getServerPort();
        if (serverPort == -1) {
            if ("https".equals(request.getScheme()))
                serverPort = 443;
            else
                serverPort = 80;
        }
        int urlPort = url.getPort();
        if (urlPort == -1) {
            if ("https".equals(url.getProtocol()))
                urlPort = 443;
            else
                urlPort = 80;
        }
        //端口不一样,无法重写
        if (serverPort != urlPort)
            return false;

//后续写        String contextPath = getContext().getPath();
//后续写        if ((contextPath != null) && (contextPath.length() > 0)) {
        String file = url.getFile();
//后续写            if ((file == null) || !file.startsWith(contextPath))
//后续写                return (false);
        if(file == null)
            return false;
        //已经有sessionid了,无法重写
        if(file.contains(";jsessionid=" + session.getId()))
            return false;
//        }

        return true;
    }
    //------------------------------------------------------------------
    //连接器
    protected HttpConnector connector = null;
    //请求对象
    protected HttpRequestImpl request = null;
    //响应状态
    protected int status = HttpServletResponse.SC_OK;
    //响应状态对应的信息
    protected String message = ResponseUtil.getStatusMessage(HttpServletResponse.SC_OK);
    //存放响应头
    protected HashMap<String, ArrayList<String>> headers = new HashMap<>();
    //存放要发送的Cookie
    protected ArrayList<Cookie> cookies = new ArrayList<>();
    //字符编码格式
    protected String encoding = Constants.DEFAULT_RESPONSE_ENCODING;
    //MINE类型
    protected String contentType = null;
    //内容长度
    protected int contentLength = -1;
    //地区
    protected Locale locale = Locale.getDefault();
    //时间表示格式
    protected final SimpleDateFormat format =
            new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
    //响应行和响应头是否已经发送?
    protected boolean committed = false;
    //缓冲区
    protected byte[] buffer = new byte[Constants.DEFAULT_OUTPUT_STREAM_BUFFER_SIZE];
    //当前缓冲区字节数
    protected int bufferCount = 0;
    //总共写了多少字节
    protected int contentCount = 0;
    //从Socket拿到的输出流
    protected OutputStream output = null;
    //封装后的输出流
    protected ServletOutputStream stream = null;
    //封装后的Writer
    protected PrintWriter writer = null;
    //允许分块发送?
    protected boolean allowChunking = false;
    //响应流
    protected HttpResponseStream responseStream = null;
    //是否发送错误报文
    protected boolean error = false;
    //外观类
    protected HttpServletResponse responseFacade = null;
    public HttpResponseBase() {
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
    }
    //创建输出流
    public ServletOutputStream createOutputStream() throws IOException {
        responseStream = new HttpResponseStream((HttpResponseImpl) this);
        return responseStream;
    }


    public void write(int b) throws IOException {
        if (suspended)
            throw new IOException("输出流被暂停");
        if (bufferCount >= buffer.length)
            ((HttpResponseImpl)this).flushBuffer();
        buffer[bufferCount++] = (byte)b;
        contentCount++;

    }
    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
    }
    public void write(byte b[], int off, int len) throws IOException {
        if (suspended)
            throw new IOException("输出流被暂停");
        if (len == 0)
            return;

        //缓冲区存得下
        if (len <= (buffer.length - bufferCount)) {
            System.arraycopy(b, off, buffer, bufferCount, len);
            bufferCount += len;
            contentCount += len;
            return;
        }
        //存不下,刷新缓冲区
        ((HttpResponseImpl)this).flushBuffer();
        //计算还要写满缓冲区多少次
        int count = len / buffer.length;
        //递归写缓冲区
        for (int i = 0; i < count; i++) {
            write(b, i * buffer.length + off, buffer.length);
        }
        //最后剩下没写完的数据
        int leftoverLen = len - buffer.length * count;
        if (leftoverLen > 0) {
            write(b, count * buffer.length + off, leftoverLen);
        }
    }

    //将给入的location转化为url形式的绝对路径
    //如果location不是绝对路径,使用服务器的根路径(http://host:port/)与location拼接返回
    protected String toAbsolute(String location) {
        if (location == null)
            return location;
        URL url = null;
        try {
            url = new URL(location);
        } catch (MalformedURLException e) {
            String reqUrl = HttpUtils.getRequestURL(request).toString();
            try {
                url = new URL(new URL(reqUrl), location);
            } catch (MalformedURLException e1) {
                throw new IllegalArgumentException(location);
            }
        }
        return url.toExternalForm();
    }
    //重写url,加上sessionId
    protected String toEncoded(String url, String sessionId) {
        if (url == null || sessionId == null)
            return url;
        String path = url;
        String query = "";
        String anchor = "";
        int questionIndex = url.indexOf('?');
        if (questionIndex >= 0) {
            path = url.substring(0, questionIndex);
            query = url.substring(questionIndex);
        }
        //例如http://www.example.com/index.html#print
        int poundIndex = path.indexOf('#');
        if (poundIndex >= 0) {
            anchor = path.substring(poundIndex);
            path = path.substring(0, poundIndex);
        }
        StringBuffer sb = new StringBuffer(path);
        if(sb.length() > 0) { // jsessionid can't be first.
            sb.append(";jsessionid=");
            sb.append(sessionId);
        }
        sb.append(anchor);
        sb.append(query);
        return sb.toString();

    }
    //结束响应,把要发的数据发出去
    public void finishResponse() throws IOException {
        HttpResponseImpl response = (HttpResponseImpl)this;

        if (response.getStatus() < HttpServletResponse.SC_BAD_REQUEST) {
            //不是错误码,正常处理
            if (!isStreamInitialized() && getContentLength() == -1
                    && response.getStatus() >= 200
                    && response.getStatus() != HttpServletResponse.SC_NOT_MODIFIED
                    && response.getStatus() != HttpServletResponse.SC_NO_CONTENT){
                response.setContentLength(0);
            }
        } else {
            //错误码,关闭连接
            response.setHeader("Connection", "close");
        }
        //如果在没有内容的情况下创建了HTTP错误报文(>=400),尝试创建简单的错误消息
        if (!response.isCommitted() &&
                stream == null && writer == null &&
                status >= HttpServletResponse.SC_BAD_REQUEST &&
                contentType == null &&
                contentCount == 0) {
            try {
                //写的内容会缓存到缓冲区
                response.setSuspended(false);
                response.setContentType("text/html");
                PrintWriter writer = response.getWriter();
                writer.println("<html>");
                writer.println("<head>");
                writer.println("<title>WebServer Error Report</title>");
                writer.println("<br><br>");
                writer.println("<h1>HTTP Status ");
                writer.print(status);
                writer.print(" - ");
                if (message != null)
                    writer.print(message);
                else
                    writer.print(ResponseUtil.getStatusMessage(status));
                writer.println("</h1>");
                writer.println("</body>");
                writer.println("</html>");
            } catch (IOException e) {
                throw e;
            } catch (Throwable e) {
                ;
            }
        }
        //发送请求行,请求头,空行
        sendLineAndHeaders();

        if (this.stream == null) {
            ServletOutputStream sos = response.getOutputStream();
            //flush不一定刷新缓冲区
            sos.flush();
            //close会刷新缓冲区,发送完所有的数据
            sos.close();
            return;
        }
        if (((ResponseStream) stream).isClosed())
            return;
        if (writer != null) {
            writer.flush();
            writer.close();
        } else {
            stream.flush();
            stream.close();
        }
    }

    //响应流是否被初始化
    public boolean isStreamInitialized() {
        return (responseStream != null);
    }
    //清除数据,重新使用响应对象
    public void reuse() {
        request = null;
        status = HttpServletResponse.SC_OK;
        message = ResponseUtil.getStatusMessage(HttpServletResponse.SC_OK);
        headers.clear();
        cookies.clear();
        encoding = Constants.DEFAULT_RESPONSE_ENCODING;
        contentType = null;
        contentLength = -1;
        locale = Locale.getDefault();
        committed = false;
        bufferCount = 0;
        contentCount = 0;
        output = null;
        stream = null;
        writer = null;
        allowChunking = false;
        responseStream = null;
        error = false;
        suspended = false;
        included = false;

    }
    //getter---------------------------------------------------------------
    public boolean isAllowChunking() {
        return this.allowChunking;
    }
    public HttpRequestImpl getRequest() {
        return request;
    }
    public OutputStream getStream() {
        return this.output;
    }
    public int getContentLength() {
        return this.contentLength;
    }
    private String getProtocol() {
        return request.getProtocol();
    }
    public boolean isSuspended() {
        return this.suspended;
    }

    public HttpServletResponse getResponseFacade() {
        if (responseFacade == null)
            responseFacade = new HttpResponseFacade((HttpServletResponse) this);
        return responseFacade;
    }
    //setter---------------------------------------------------------------

    public void setSuspended(boolean suspended) {
        this.suspended = suspended;
        if (stream != null)
            ((ResponseStream) stream).setSuspended(suspended);
    }
    public void setError() {
        this.error = true;
    }
    public void setConnector(HttpConnector connector) {
        this.connector = connector;
    }
    public void setStream(OutputStream output) {
        this.output = output;
    }
    public void setRequest(HttpRequestImpl request) {
        this.request = request;
    }
    public void setAllowChunking(boolean allowChunking) {
        this.allowChunking = allowChunking;
    }




}

